<template>
  <div class="call-container" v-if="dialling || calling || isDialled">
    <div class="choose" v-if="isDialled">
      <div class="title">
        {{ toAccount }}&nbsp;来电话了
      </div>
      <div class="buttons">
        <div class="accept" @click="accept"></div>
        <div class="refuse" @click="refuse"></div>
      </div>
    </div>
    <div class="call" v-if="dialling || calling">
      <div class="title" v-if="dialling">
        正在呼叫&nbsp;{{ toAccount }}...
      </div>
      <div id="local" @click="changeMainVideo" :class="isLocalMain ? 'small' : 'big'" v-show="calling"></div>
      <div name="remote" :class="isLocalMain ? 'big' : 'small'" @click="changeMainVideo" v-show="calling"></div>
      <div class="duration" v-show="calling">
        {{ formatDurationStr }}
      </div>
      <div class="buttons">
        <div :class="isCamOn ? 'videoOn' : 'videoOff'" @click="videoHandler"></div>
        <div class="refuse" @click="leave"></div>
        <div :class="isMicOn ? 'micOn' : 'micOff'" @click="micHandler"></div>
      </div>
      <div class="mask" v-show="maskShow" :class="isLocalMain ? 'small' : 'big'" @click="changeMainVideo">
        <div>
          <img class="image" src="../../assets/image/camera-max.png"/>
          <p class="notice">摄像头未打开</p>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import RtcClient from '../../utils/rtc-client'
import { ACTION, VERSION } from '../../utils/trtcCustomMessageMap'
import { mapGetters, mapState } from 'vuex'
import { formatDuration } from '../../utils/formatDuration'

export default {
  name: 'CallLayer',
  data() {
    return {
      Trtc: undefined,
      isCamOn: true,
      isMicOn: true,
      maskShow: false,
      isLocalMain: true, // 本地视频是否是主屏幕显示
      start: 0,
      end: 0,
      duration: 0,
      hangUpTimer: 0, // 通话计时id
      ready: false,
      dialling: false, // 是否拨打电话中
      calling: false, // 是否通话中
      isDialled: false, // 是否被呼叫
    }
  },
  computed: {
    ...mapGetters(['toAccount', 'currentConversationType']),
    ...mapState({
      userID: state => state.user.userID,
      userSig: state => state.user.userSig,
      videoRoom: state => state.video.videoRoom,
      sdkAppID: state => state.user.sdkAppID
    }),
    formatDurationStr() {
      return formatDuration(this.duration)
    },
  },
  created() {
    window.addEventListener('beforeunload', () => {
      this.videoCallLogOut()
    })
    window.addEventListener('leave', () => {
      this.videoCallLogOut()
    })
  },
  mounted() {
    this.$bus.$on('isCalled', this.isCalled)
    this.$bus.$on('missCall', this.missCall)
    this.$bus.$on('isRefused', this.isRefused)
    this.$bus.$on('isAccept', this.isAccept)
    this.$bus.$on('isHungUp', this.isHungUp)
    this.$bus.$on('busy', this.busy)
    this.$bus.$on('video-call', this.videoCall)
  },
  beforeDestroy() {
    this.$bus.$off('isCalled', this.isCalled)
    this.$bus.$off('missCall', this.missCall)
    this.$bus.$off('isRefused', this.isRefused)
    this.$bus.$off('isAccept', this.isAccept)
    this.$bus.$off('isHungUp', this.isHungUp)
    this.$bus.$off('busy', this.busy)
    this.$bus.$off('video-call', this.videoCall)
  },
  methods: {
    videoCallLogOut() { // 针对，刷新页面，关闭Tab，登出情况下，通话断开的逻辑
      if (this.dialling || this.calling) {
        this.leave()
      }
      if (this.isDialled) {
        this.refuse()
      }
    },
    changeState(state, boolean) {
      let stateList = ['dialling', 'isDialled', 'calling']
      stateList.forEach(item => {
        this[item] = item === state ? boolean : false
      })
      this.$store.commit('UPDATE_ISBUSY', stateList.some(item => this[item])) // 若stateList 中存在 true , isBusy 为 true
    },
    async initTrtc(options) { // 初始化 trtc 进入房间
      this.Trtc = new RtcClient(options)
      await this.Trtc.createLocalStream({ audio: true, video: true }).then(() => { // 在进房之前，判断设备
          this.Trtc.join()
          this.ready = true
          this.isCamOn = true
          this.maskShow = false
      }).catch(() => {
        alert(
          '请确认已连接摄像头和麦克风并授予其访问权限！'
        )
        this.ready = false
      })
    },
    videoCall() { // 发起通话
      if (this.calling) { // 避免通话按钮多次快速的点击
        return
      }
      this.isLocalMain = true
      this.$store.commit('GENERATE_VIDEO_ROOM') // 初始化房间号
      const options = {
        userId: this.userID,
        userSig: this.userSig,
        roomId: this.videoRoom,
        sdkAppId: this.sdkAppID
      }
      this.initTrtc(options).then(() => {
        if (!this.ready) return
        this.changeState('dialling', true)
        this.timer = setTimeout(this.timeout, process.env.NODE_ENV === 'development' ? 999999 : 60000) // 开始计时器，开发环境超时时间较长，便于调试
        this.sendVideoMessage(ACTION.VIDEO_CALL_ACTION_DIALING)
      })
    },
    leave() { // 离开房间，发起方挂断
      if (!this.calling) { // 还没有通话，单方面挂断
        this.Trtc.leave()
        clearTimeout(this.timer)
        this.changeState('dialling', false)
        this.sendVideoMessage(ACTION.VIDEO_CALL_ACTION_SPONSOR_CANCEL)
        return
      }
      this.hangUp() // 通话一段时间之后，某一方面结束通话
    },
    timeout() { // 通话超时
      this.changeState('dialling', false)
      this.Trtc.leave()
      this.sendVideoMessage(ACTION.VIDEO_CALL_ACTION_SPONSOR_TIMEOUT)
    },
    isCalled() { // 被呼叫
      this.changeState('isDialled', true)
    },
    missCall() { // 错过电话，也就是发起方的电话超时挂断或自己挂断
      this.changeState('isDialled', false)
    },
    refuse() { // 拒绝电话
      this.changeState('isDialled', false)
      this.sendVideoMessage(ACTION.VIDEO_CALL_ACTION_REJECT)
    },
    isRefused() { // 对方拒绝通话
      this.changeState('dialling', false)
      clearTimeout(this.timer)
    },
    resetDuration(duration) {
      this.duration = duration
      this.hangUpTimer = setTimeout(() => {
        let now = new Date()
        this.resetDuration(parseInt((now - this.start) / 1000))
      }, 1000)
    },
    accept() { // 接听电话
      this.changeState('calling', true)
      const options = {
       userId: this.userID,
        userSig: this.userSig,
        roomId: this.videoRoom,
        sdkAppId: this.sdkAppID
      }
      this.initTrtc(options).then(() => {
        if (!this.ready) {
          this.changeState('calling', false)
          this.sendVideoMessage(ACTION.VIDEO_CALL_ACTION_ERROR)
          return
        }
        this.sendVideoMessage(ACTION.VIDEO_CALL_ACTION_ACCEPTED)
        this.start = new Date()
        clearTimeout(this.hangUpTimer)
        this.resetDuration(0)
      })
    },
    isAccept() { // 对方接听自己发起的电话
      clearTimeout(this.timer)
      this.changeState('calling', true)
      clearTimeout(this.hangUpTimer)
      this.resetDuration(0)
      this.start = new Date()
    },
    hangUp() { // 通话一段时间之后，某一方挂断电话
      this.changeState('calling', false)
      this.Trtc.leave()
      this.end = new Date()
      const duration = parseInt((this.end - this.start) / 1000)
      this.sendVideoMessage(ACTION.VIDEO_CALL_ACTION_HANGUP, duration)
      clearTimeout(this.hangUpTimer)
    },
    isHungUp() { // 通话一段时间之后，对方挂断电话
      if (this.calling) {
        this.changeState('calling', false)
        this.Trtc.leave()
        clearTimeout(this.hangUpTimer)
      }
    },
    busy(videoPayload, messageItem) {
      videoPayload.action = ACTION.VIDEO_CALL_ACTION_LINE_BUSY
      const message = this.tim.createCustomMessage({
        to: messageItem.from,
        conversationType: this.currentConversationType,
        payload: {
          data: JSON.stringify(videoPayload),
          description: '',
          extension: ''
        }
      })
      this.$store.commit('pushCurrentMessageList', message)
      this.tim.sendMessage(message)
    },
    videoHandler() { // 是否打开摄像头
      if (this.isCamOn) {
        this.isCamOn = false
        this.maskShow = true
        this.Trtc.muteLocalVideo()
      } else {
        this.isCamOn = true
        this.maskShow = false
        this.Trtc.unmuteLocalVideo()
      }
    },
    micHandler() { // 是否打开麦克风
      if (this.isMicOn) {
        this.isMicOn = false
        this.Trtc.muteLocalAudio()
      } else {
        this.isMicOn = true
        this.Trtc.unmuteLocalAudio()
      }
    },
    sendVideoMessage(action, duration = 0) {
      const options = {
        room_id: this.videoRoom,
        call_id: '',
        action,
        version: VERSION,
        invited_list: [],
        duration
      }
      const message = this.tim.createCustomMessage({
        to: this.toAccount,
        conversationType: this.currentConversationType,
        payload: {
          data: JSON.stringify(options),
          description: '',
          extension: ''
        }
      })
      this.$store.commit('pushCurrentMessageList', message)
      this.tim.sendMessage(message)
    },
    changeMainVideo() {
      if (!this.calling) {
        return
      }
      this.isLocalMain = !this.isLocalMain
    }
  }
}
</script>

<style lang="stylus" scoped>
.call-container
  background center url("")
  background-size cover
  position absolute
  z-index 999
.accept, .refuse, .videoOn, .videoOff, .micOn, .micOff
  height 50px
  width 50px
  box-sizing border-box
  border-radius 50%
  cursor:pointer
.accept
  background center no-repeat url("../../assets/image/call.png")
  background-size 60%
  background-color $success
.refuse
  background center no-repeat url("../../assets/image/close.png")
  background-size 70%
  background-color $danger
.videoOn
  background center no-repeat url("../../assets/image/big-camera-on.png")
.videoOff
  background center no-repeat url("../../assets/image/big-camera-off.png")
.micOn
  background center no-repeat url("../../assets/image/big-mic-on.png")
.micOff
  background center no-repeat url("../../assets/image/big-mic-off.png")
.buttons
  position absolute
  z-index 20
  width 70%
  top 75%
  display flex
  justify-content space-around
  margin 0 15% 0 15%
.duration
  color #fff
  position absolute
  z-index 20
  width 100%
  top 70%
  display flex
  justify-content center
.mask
  position absolute
  z-index 10
  background #D8D8D8
  height 100%
  width 100%
  display flex
  align-items center
  justify-content center
  space
  .image
    margin-left 15%
  .notice
    color #888888
.choose, .call
  color #fff
  background-color rgba(0, 0, 0, 0.8)
  height 100%
  width 100%
.title
  margin 25% 0 0 0
  text-align center
  width 100%
  position absolute
  z-index 10
  color  #fff
  font-size 40px
  font-weight 700
.big
  position absolute
  height 100%
  width 100%
.small
  position absolute
  margin-left 74.8%
  z-index 999
  border-style solid
  border-width 1px
  border-color #808080
  height 44.8%
  width 25.2%
</style>